package com.google.code.fulltank.strategy;

import com.google.code.fulltank.FuelTank;
import com.google.code.fulltank.price.FuelPriceHistory;
import com.google.code.fulltank.usage.FuelUsageHistory;
import org.joda.time.LocalDate;

/**
 * Implements the optimal "clairvoyant" strategy: fill up the tank completely
 * when prices are about to go up. If prices are about to go down, fill up only
 * the minimum needed to survive till the next price update.
 *
 * @author DL
 */
public class ClairvoyantStrategy extends FuelingStrategy {
    public ClairvoyantStrategy(
        FuelTank tank, FuelPriceHistory prices, FuelUsageHistory usage) {

        super(tank, prices, usage);
    }


    @Override
    public double onDate(LocalDate date) {
        // Get current price
        double price = 0;
        LocalDate priceDate = date;
        while (true) {
            Double priceTmp = prices.getUpdatedFuelPrice(priceDate);
            if (priceTmp != null) {
                price = priceTmp;
                break;
            }
            priceDate = priceDate.minusDays(1);
        }

        // Get next price
        double nextPrice = price;
        int daysToNextPrice = 0;
        LocalDate nextPriceDate = date.plusDays(1);
        while (!nextPriceDate.isAfter(usage.getLastDate())) {
            ++daysToNextPrice;
            Double nextPriceTmp = prices.getUpdatedFuelPrice(nextPriceDate);
            if (nextPriceTmp != null) {
                nextPrice = nextPriceTmp;
                break;
            }
            nextPriceDate = nextPriceDate.plusDays(1);
        }

        if ((daysToNextPrice == 1) && (nextPrice > price)) {
            return Double.POSITIVE_INFINITY;
        } else if (tank.getFuelAmount() < usage.getFuelUsage(date)) {
            if (nextPrice > price) {
                return Double.POSITIVE_INFINITY;
            } else {
                double litresToNextPriceDate = 0;
                LocalDate tmpDate = date;
                while (tmpDate.isBefore(nextPriceDate)) {
                    litresToNextPriceDate += usage.getFuelUsage(tmpDate);
                    tmpDate = tmpDate.plusDays(1);
                }

                // workaround for numerical errors
                return (double)Math.round(
                    1000*(litresToNextPriceDate - tank.getFuelAmount()))/1000;
            }
        }
        
        return 0;
    }
}
